package com.britoso;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Desktop;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.swing.JButton;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.SwingConstants;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableModel;
import javax.swing.table.TableRowSorter;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import java.awt.FlowLayout;
import javax.swing.JMenuBar;
import javax.swing.JMenu;

/**
 * TODOs: Add database. Show database stats at startup Allow database searches
 * Allow database additions and deletions
 * 
 * 
 * @author sonal.f.brito
 * 
 */
public class MovieInfo implements ActionListener
{
	private final Logger logger = Logger.getLogger(Thread.currentThread().getStackTrace()[0].getClassName());
	private static final String NEWLINE = "\n";
	private static final String VERSION = " v0.7";
	private static final long MIN_FILE_SIZE_BYTES = 524288000;// 500 MB
	private static final String APP_NAME = "Movie Info";
	private static final String QUOTE = "\"";
	static String[] TABLE_COLS = { "File name", "Keyword", "Title", "Year", "Rating", "Genre", "Runtime", "Language", "Country", "Link", "Size" };
	private JFrame frmMovieInfo;
	private JTextField folderTextField;
	public static JTable table;
	private static JTextArea logTextArea;
	private JButton searchButton;
	static DefaultTableModel model = new DefaultTableModel(null, TABLE_COLS);
	JPopupMenu popup;

	class PopupListener extends MouseAdapter
	{
		public void mousePressed(MouseEvent e)
		{
			// maybeShowPopup(e);
		}

		public void mouseReleased(MouseEvent e)
		{
			JTable source = (JTable) e.getSource();
			int row = source.rowAtPoint(e.getPoint());
			int column = source.columnAtPoint(e.getPoint());

			if (!source.isRowSelected(row))
				source.changeSelection(row, column, false, false);

			maybeShowPopup(e);
		}

		private void maybeShowPopup(MouseEvent e)
		{
			if (e.isPopupTrigger())
			{
				popup.show(e.getComponent(), e.getX(), e.getY());
			}
		}
	}

	/**
	 * Launch the application.
	 */
	public static void main(String[] args)
	{
		EventQueue.invokeLater(new Runnable()
		{
			public void run()
			{
				try
				{
					MovieInfo window = new MovieInfo();
					window.frmMovieInfo.setVisible(true);
				}
				catch (Exception e)
				{
					e.printStackTrace();
				}
			}
		});
	}

	/**
	 * Create the application.
	 */
	public MovieInfo()
	{
		initialize();
		// TODO: remove the next line

		// loadFilesfromFolder("f:\\movies");
	}

	/**
	 * Initialize the contents of the frame.
	 */
	private void initialize()
	{
		frmMovieInfo = new JFrame();

		frmMovieInfo.setTitle(APP_NAME + " " + VERSION);
		frmMovieInfo.setBounds(100, 100, 922, 609);
		frmMovieInfo.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		frmMovieInfo.getContentPane().setLayout(new BorderLayout());
		frmMovieInfo.getContentPane().setBackground(new Color(56, 56, 99));

		JPanel searchPanel = new JPanel(new BorderLayout());
		JLabel lblChooseAMovie = new JLabel("Choose a movie folder");
		lblChooseAMovie.setRequestFocusEnabled(false);
		folderTextField = new JTextField();
		folderTextField.setMinimumSize(new Dimension(6, 23));
		folderTextField.setPreferredSize(new Dimension(100, 25));
		folderTextField.setColumns(50);

		searchButton = new JButton("Search");
		searchButton.setBackground(new Color(113, 113, 198));
		searchButton.setForeground(Color.WHITE);
		searchButton.addActionListener(this);
		searchPanel.add(lblChooseAMovie, BorderLayout.WEST);

		searchPanel.add(folderTextField, BorderLayout.CENTER);

		JPanel buttonPanel = new JPanel();
		FlowLayout flowLayout = (FlowLayout) buttonPanel.getLayout();
		flowLayout.setVgap(0);
		flowLayout.setHgap(0);
		searchPanel.add(buttonPanel, BorderLayout.EAST);
		JButton btnBrowse = new JButton("Browse/Add");
		btnBrowse.setVerticalAlignment(SwingConstants.TOP);
		buttonPanel.add(btnBrowse);
		btnBrowse.setBackground(Color.GREEN);
		btnBrowse.setForeground(Color.WHITE);
		btnBrowse.setRequestFocusEnabled(false);

		JButton clearButton = new JButton("Clear");
		clearButton.setVerticalAlignment(SwingConstants.TOP);
		clearButton.setBackground(Color.RED);
		clearButton.setForeground(Color.WHITE);
		clearButton.addActionListener(this);
		buttonPanel.add(clearButton);
		btnBrowse.addActionListener(this);
		searchPanel.add(searchButton, BorderLayout.SOUTH);
		frmMovieInfo.getContentPane().add(searchPanel, BorderLayout.NORTH);
		searchPanel.setPreferredSize(new Dimension(400, 55));

		JScrollPane tablePanel = new JScrollPane();
		frmMovieInfo.getContentPane().add(tablePanel, BorderLayout.CENTER);

		table = new JTable();
		tablePanel.setViewportView(table);
		table.setModel(model);
		table.setRowSorter(new TableRowSorter<TableModel>(table.getModel()));
		JPanel logPanel = new JPanel(new BorderLayout());
		frmMovieInfo.getContentPane().add(logPanel, BorderLayout.SOUTH);

		// Add listener to components that can bring up popup menus.
		MouseListener popupListener = new PopupListener();
		table.addMouseListener(popupListener);

		// Create the popup menu.
		popup = new JPopupMenu();
		JMenuItem menuItem = new JMenuItem("Get Keywords from Folder(s)");
		menuItem.addActionListener(this);
		popup.add(menuItem);
		menuItem = new JMenuItem("Search for Selected");
		menuItem.addActionListener(this);
		popup.add(menuItem);
		menuItem = new JMenuItem("Rename file using keywords and year");
		menuItem.addActionListener(this);
		popup.add(menuItem);
		menuItem = new JMenuItem("Rename using seach results");
		menuItem.addActionListener(this);
		popup.add(menuItem);
		menuItem = new JMenuItem("Delete File(s)");
		menuItem.addActionListener(this);
		popup.add(menuItem);
		menuItem = new JMenuItem("Delete Folder(s)");
		menuItem.addActionListener(this);
		menuItem.setForeground(Color.RED);
		popup.add(menuItem);
		// TODO: add save to Database option.
		menuItem = new JMenuItem("Play Movie");
		menuItem.addActionListener(this);
		popup.add(menuItem);

		logTextArea = new JTextArea();
		logTextArea.setAutoscrolls(true);
		logTextArea.setBackground(Color.LIGHT_GRAY);
		JScrollPane logScrollPane = new JScrollPane(logTextArea);
		logScrollPane.setMaximumSize(new Dimension(5000, 200));
		// logTextArea.setPreferredSize(new Dimension(600, 60));
		logScrollPane.setAutoscrolls(true);
		logPanel.add(logScrollPane, BorderLayout.CENTER);

		logTextArea.setRows(5);

		JMenuBar menuBar = new JMenuBar();
		frmMovieInfo.setJMenuBar(menuBar);

		JMenu mnNewMenu = new JMenu("File");
		menuBar.add(mnNewMenu);

		JMenuItem mntmNewMenuItem = new JMenuItem("Save to Database");
		mnNewMenu.add(mntmNewMenuItem);

		JMenuItem mntmNewMenuItem_1 = new JMenuItem("Export Movie List to CSV");
		mntmNewMenuItem_1.addActionListener(new ActionListener()
		{
			public void actionPerformed(ActionEvent arg0)
			{
				saveToCSV();
			}
		});
		mnNewMenu.add(mntmNewMenuItem_1);

		JMenuItem mntmNewMenuItem_2 = new JMenuItem("Quit");
		mnNewMenu.add(mntmNewMenuItem_2);
		table.getColumnModel().getColumn(0).setPreferredWidth(600);
	}

	protected void saveToCSV()
	{
		JFileChooser chooser = new JFileChooser();
		int res = chooser.showSaveDialog(frmMovieInfo);
		if (res == JFileChooser.CANCEL_OPTION)
			return;
		File f = chooser.getSelectedFile();
		if (f.exists())
		{
			int result = JOptionPane.showConfirmDialog(frmMovieInfo, "The file exists, overwrite?", "Existing file", JOptionPane.YES_NO_CANCEL_OPTION);
			switch (result)
			{
			case JOptionPane.YES_OPTION:
				break;
			case JOptionPane.NO_OPTION:
				return;
			case JOptionPane.CLOSED_OPTION:
				return;
			case JOptionPane.CANCEL_OPTION:
				return;
			}
		}
		// save
		TableModel model = table.getModel();
		FileWriter out;
		try
		{
			out = new FileWriter(f);
			for (int i = 0; i < model.getColumnCount(); i++)
			{
				out.write(QUOTE+model.getColumnName(i) +QUOTE+ ",");
			}
			out.write("\n");

			for (int i = 0; i < model.getRowCount(); i++)
			{
				for (int j = 0; j < model.getColumnCount(); j++)
				{
					if(model.getValueAt(i, j)!=null)
					{
						//System.out.println("writing val:"+ model.getValueAt(i, j).toString());
						out.write(QUOTE+model.getValueAt(i, j).toString()+QUOTE);
					}
					out.write(",");
				}
				out.write("\n");
			}
			out.close();
		}
		catch (IOException e)
		{
			e.printStackTrace();
		}
		System.out.println("Wrote CSV: " + f);
	}

	@Override
	public void actionPerformed(ActionEvent e)
	{
		if (e.getActionCommand().equals("Browse/Add"))
		{
			JFileChooser fileChooser = new JFileChooser();
			fileChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
			int returnVal = fileChooser.showOpenDialog(frmMovieInfo);
			if (returnVal == JFileChooser.APPROVE_OPTION)
			{
				loadFilesfromFolder(fileChooser.getSelectedFile().getAbsolutePath());
			}
		}
		else if (e.getActionCommand().equals("Search"))
		{
			search();
		}
		else if (e.getActionCommand().equals("Clear"))
		{
			for (int i = model.getRowCount() - 1; i >= 0; i--)
			{
				model.removeRow(i);
			}
			model.fireTableDataChanged();
		}
		else if (e.getActionCommand().equals("Delete File(s)") || e.getActionCommand().equals("Delete Folder(s)")
				|| e.getActionCommand().equals("Get Keywords from Folder(s)") || e.getActionCommand().equals("Rename file using keywords and year")
				|| e.getActionCommand().equals("Rename using seach results"))
		{
			processSelectedRows(e.getActionCommand());
		}

		else if (e.getActionCommand().equals("Play Movie"))
		{
			int[] rows = table.getSelectedRows();
			if (rows.length > 1)
			{
				JOptionPane.showMessageDialog(frmMovieInfo, "Please select only one file");
			}
			else
			{
				try
				{
					Desktop.getDesktop().open(new File(((String) table.getValueAt(rows[0], 0))));
				}
				catch (IOException e1)
				{
					System.out.println("Error playing: " + table.getValueAt(rows[0], 0));
					e1.printStackTrace();
				}
			}
		}
		else if (e.getActionCommand().equals("Search for Selected"))
		{
			new SearchManagerForRows().execute();
		}
		else
		{
			System.out.println("Uncaught Action: " + e.getActionCommand());
		}
	}

	private void processSelectedRows(String actionCommand)
	{
		int[] rows = table.getSelectedRows();
		ArrayList<String> filePaths = new ArrayList<String>();
		for (int k = 0; k < rows.length; k++)
		{
			filePaths.add((String) table.getValueAt(rows[k], 0));
			System.out.println("Selected: " + filePaths.get(filePaths.size() - 1));
		}
		int[] selectedRows = null;
		switch (actionCommand)
		{
		case "Rename using seach results":
			selectedRows = table.getSelectedRows();
			if (selectedRows.length > 0)
			{
				for (int i = 0; i < selectedRows.length; i++)
				{
					String filename = (String) model.getValueAt(table.convertRowIndexToModel(selectedRows[i]), 0);
					String title = (String) model.getValueAt(table.convertRowIndexToModel(selectedRows[i]), 2);
					String year = (String) model.getValueAt(table.convertRowIndexToModel(selectedRows[i]), 3);
					String imdbScore = (String) model.getValueAt(table.convertRowIndexToModel(selectedRows[i]), 4);

					String ext = FilenameUtils.getExtension(filename);
					String path = FilenameUtils.getFullPath(filename);
					StringBuffer newname = new StringBuffer();
					newname.append(path);

					if (title == null)
					{
						System.out.println("Title=null");
					}
					if (title != null && title.length() > 0)
					{
						newname.append(FileNameCleaner.cleanFileName(title));
						if (year.length() > 0)
						{
							newname.append(" ");
							newname.append("(");
							newname.append(year);
							newname.append(")");
							newname.append(" ");
						}
						if (imdbScore.length() > 0)
						{
							newname.append("[");
							newname.append(imdbScore);
							newname.append("]");
						}
						newname.append(".");
						newname.append(ext);

						File f = new File(filename);
						File fnew = new File(newname.toString());
						log("Renaming " + f.getAbsolutePath() + " to " + fnew.getAbsolutePath());
						f.renameTo(fnew);
						model.setValueAt(newname.toString(), table.convertRowIndexToModel(selectedRows[i]), 0);
					}
				}
				model.fireTableDataChanged();
			}
			break;
		case "Delete File(s)":
			for (String file : filePaths)
			{
				File f = new File(file);
				if (f.isFile())
				{
					int choice = JOptionPane.showConfirmDialog(frmMovieInfo, (Object) "Delete file: " + f.getAbsolutePath(), "Verify file delete",
							JOptionPane.YES_NO_OPTION);
					if (choice == JOptionPane.YES_OPTION)
					{
						log("Deleting file: " + f.getAbsolutePath());
						f.delete();
					}
				}
			}
			removeRows();
			break;
		case "Delete Folder(s)":
			for (String file : filePaths)
			{
				File f = new File(FilenameUtils.getFullPath(file));
				if (f.isDirectory())
				{
					int choice = JOptionPane.showConfirmDialog(frmMovieInfo, (Object) "Delete folder: " + f.getAbsolutePath(), "Verify folder delete",
							JOptionPane.YES_NO_OPTION);
					if (choice == JOptionPane.YES_OPTION)
					{
						try
						{
							log("Deleting dir: " + f.getAbsolutePath());
							FileUtils.deleteDirectory(f);
						}
						catch (IOException e)
						{
							e.printStackTrace();
						}
					}// if
				}
			}
			removeRows();
			break;
		case "Get Keywords from Folder(s)":
			selectedRows = table.getSelectedRows();
			if (selectedRows.length > 0)
			{
				for (int i = 0; i < selectedRows.length; i++)
				{
					String path = (String) table.getValueAt(selectedRows[i], 0);
					File f = new File(path);
					getKeywordsFromString(f.getParentFile().getName(), table.convertRowIndexToModel(selectedRows[i]));
				}
			}
			break;
		case "Rename file using keywords and year":
			selectedRows = table.getSelectedRows();
			if (selectedRows.length > 0)
			{
				for (int i = 0; i < selectedRows.length; i++)
				{
					String path = (String) table.getValueAt(selectedRows[i], 0);
					String extension = FilenameUtils.getExtension(path);
					StringBuffer newName = new StringBuffer();
					newName.append(FilenameUtils.getFullPath(path));
					String keyword = (String) table.getValueAt(selectedRows[i], 1);
					newName.append(FileNameCleaner.cleanFileName(keyword));
					if (table.getValueAt(selectedRows[i], 3) != null)
					{
						String year = ((String) table.getValueAt(selectedRows[i], 3)).trim();
						if (year.length() > 0)
						{
							newName.append(" ");
							newName.append("(");
							newName.append(year);
							newName.append(")");
						}
					}
					newName.append(".");
					newName.append(extension);
					File f = new File(path);
					log("Renaming " + f.getAbsolutePath() + " to " + newName);
					f.renameTo(new File(newName.toString()));
					model.setValueAt(newName.toString(), table.convertRowIndexToModel(selectedRows[i]), 0);
				}
			}
			break;
		}

	}

	private void removeRows()
	{
		// delete the rows if needed, last to first
		int[] selectedRows = table.getSelectedRows();
		if (selectedRows.length > 0)
		{
			for (int i = selectedRows.length - 1; i >= 0; i--)
			{
				model.removeRow(table.convertRowIndexToModel(selectedRows[i]));
			}
		}
	}

	public static String humanReadableByteCount(long bytes, boolean si) {
	    int unit = si ? 1000 : 1024;
	    if (bytes < unit) return bytes + " B";
	    int exp = (int) (Math.log(bytes) / Math.log(unit));
	    String pre = (si ? "kMGTPE" : "KMGTPE").charAt(exp-1) + (si ? "" : "i");
	    return String.format("%.1f %sB", bytes / Math.pow(unit, exp), pre);
	}
	
	private void loadFilesfromFolder(String path)
	{
		File folder = new File(path);
		// This is where a real application would open the file.
		log("Opening: " + folder.getAbsolutePath());
		folderTextField.setText(folder.getAbsolutePath());
		// get a list of files
		ArrayList<String> fileList = new ArrayList<String>();
		getFiles(folder.getAbsolutePath(), fileList);

		DefaultTableModel model = (DefaultTableModel) table.getModel();

		String[] values = new String[11];
		File file;
		for (String f : fileList)
		{
			values[0] = f;
			//file size
			file=new File(f);
			values[10]=humanReadableByteCount(file.length(),false);			
			model.addRow(values);
		}
		log("Completed scanning folder " + path);
		setKeywords();
		log("Keywords extracted from filenames.");
	}

	private void search()
	{
		new SearchManagerForTable().execute();

	}

	private void setKeywords()
	{
		DefaultTableModel model = (DefaultTableModel) table.getModel();
		String filepath;
		for (int i = 0; i < model.getRowCount(); i++)
		{
			filepath = (String) model.getValueAt(i, 0);
			filepath = filepath.substring(filepath.lastIndexOf(System.getProperty("file.separator")) + 1, filepath.lastIndexOf("."));
			filepath = filepath.trim();
			getKeywordsFromString(filepath, i);
		}

	}

	/**
	 * From the input string, set the keywords and year
	 * 
	 * @param fileName
	 * @param row
	 */
	private void getKeywordsFromString(String fileName, int row)
	{
		final String[] knownStopWords = { "DVD", "cd1", "cd2", "DIVX", "XVID", "BLURAY", "BRRIP", "subs", "sample", "720p" };
		final char[] badChars = { '.', '-', '[', ']', '(', ')', '_' };

		logger.finest("\nMovie: " + fileName);

		// find the pattern [x.y] and delete it, its the imdb score.
		Pattern pattern = Pattern.compile("\\[\\d\\.\\d\\]");// 7.2
		Matcher matcher = pattern.matcher(fileName);
		if (matcher.find())
		{
			// System.out.println("Found:" + matcher.group(0) + " in "
			// +fileName);
			model.setValueAt(matcher.group(0), row, 4);
			fileName = fileName.replace(matcher.group(0), "");
			// System.out.println("new Filename:" +fileName);
		}

		for (int j = 0; j < badChars.length; j++)
		{
			fileName = fileName.replace(badChars[j], ' ');
		}
		logger.finest("Movie (bad chars removed): " + fileName);

		// check if there is a 4 digit year
		pattern = Pattern.compile("(19|20)\\d{2}");// 1900-2099
		matcher = pattern.matcher(fileName);
		if (matcher.find())
		{
			// System.out.println("Found:" + matcher.group(0) + " in "
			// +movieName);
			model.setValueAt(matcher.group(0), row, 3);// found a year, use it
														// in the search for
														// better results
			fileName = fileName.replace(matcher.group(0), "");// remove the year
		}
		logger.finest("Movie (year removed): " + fileName);

		for (int j = 0; j < knownStopWords.length; j++)
		{
			int pos = fileName.toLowerCase().indexOf(knownStopWords[j].toLowerCase());
			if (pos > 0)
			{
				fileName = fileName.substring(0, pos - 1);
				// break;
			}
		}

		fileName = fileName.replaceAll(" +", " ");// replace 2 or more
													// spaces with one
		logger.finest("Movie (stop words processed): " + fileName);
		model.setValueAt(fileName, row, 1);
	}

	// recursive
	private void getFiles(String folder, ArrayList<String> fileList)
	{
		File dir = new File(folder);
		File[] files = dir.listFiles(new FileFilter());
		long fileSize;
		for (File f : files)
		{
			if (f.isDirectory())
			{
				getFiles(f.getAbsolutePath(), fileList);// recursive
			}
			else
			{
				fileSize = 0;
				if (f.exists() && f.isFile() && f.canRead())
				{
					fileSize = f.length();
				}
				if (fileSize > MIN_FILE_SIZE_BYTES)
				{
					fileList.add(f.getAbsolutePath());
				}
			}
		}
	}

	public static void log(String string)
	{
		if (logTextArea != null)
		{
			String ln = string;
			if (logTextArea.getLineCount() > 0)
			{
				ln = logTextArea.getText().trim() + NEWLINE + string;
			}
			logTextArea.setText(ln);
		}
	}
}
